#pragma once
#include "Symbol/TypeSymbol.hpp"
#include "Syntax/Kind.hpp"

#include <map>
#include <memory>
#include <string>
#include <vector>

enum class BoundBinaryOperatorKind : int
{
    Error,
    Addition,
    Subtraction,
    Multiplication,
    Division,
    Remainder,
    LogicalAnd,
    LogicalOr,
    BitwiseOR,
    BitwiseAnd,
    BitwiseXor,
    Equals,
    NotEquals,
    Less,
    LessOrEquals,
    Greater,
    GreaterOrEquals,
};
class BoundBinaryOperator;
typedef std::shared_ptr<BoundBinaryOperator> SharedBoundBinaryOperator;

class BoundBinaryOperator
{
public:
    SyntaxKind              SyntaxKindTo;
    BoundBinaryOperatorKind Kind;

    SharedTypeSymbol LeftType;
    SharedTypeSymbol RightType;
    SharedTypeSymbol Type;

public:
    BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, SharedTypeSymbol type) : BoundBinaryOperator(syntaxKind, kind, type, type, type) {}

    BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, SharedTypeSymbol operandType, SharedTypeSymbol resultType)
        : BoundBinaryOperator(syntaxKind, kind, operandType, operandType, resultType)
    {
    }

    BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, SharedTypeSymbol leftType, SharedTypeSymbol rightTye, SharedTypeSymbol resultType)
        : SyntaxKindTo(syntaxKind), Kind(kind), LeftType(leftType), RightType(rightTye), Type(resultType)
    {
    }

public:
    static std::vector<SharedBoundBinaryOperator> _operators;

public:
    static SharedBoundBinaryOperator Bind(SyntaxKind syntaxKind, SharedTypeSymbol leftType, SharedTypeSymbol rightType)
    {
        for (auto op : _operators)
        {
            if ((op->SyntaxKindTo == syntaxKind) && (op->LeftType->eq(leftType) ) && (op->RightType->eq(rightType)))
                return op;
        }
        return nullptr;
    }
    std::string GetTypeName() const { return this->KindAllName[this->Kind]; }

private:
    static std::map<BoundBinaryOperatorKind, std::string> KindAllName;
};